type Handler = (...args: unknown[]) => void;

class Listener {
  private map: mapboxgl.Map | undefined | null;

  constructor(map: mapboxgl.Map | undefined | null) {
    this.map = map;
  }

  private listenerMap: Map<string, Record<string, Handler>> = new Map();

  add(type: string, id: string | undefined, handler: Handler) {
    if (!id) return;
    const map = this.listenerMap.get(type);
    if (map) {
      if (map[id]) {
        this.map?.off(type, [id], map[id]);
      }
      map[id] = handler;
    } else {
      this.listenerMap.set(type, { [id]: handler });
    }
    this.map?.on(type, [id], handler);
  }

  addIdList(type: string, idList: (string | undefined)[], handler: Handler) {
    idList.forEach((id) => {
      this.add(type, id, handler);
    });
  }

  addMany(type: string, idHandlers: Record<string, Handler>) {
    for (const id in idHandlers) {
      this.add(type, id, idHandlers[id]);
    }
  }

  disconnect4Map() {
    this.listenerMap.forEach((map, type) => {
      for (const id in map) {
        this.map?.off(type, [id], map[id]);
      }
    });
  }

  reconnect2Map() {
    this.listenerMap.forEach((map, type) => {
      for (const id in map) {
        this.map?.on(type, [id], map[id]);
      }
    });
  }

  remove(type: string, id: string | undefined) {
    const map = this.listenerMap.get(type);
    if (map && id) {
      if (map[id]) {
        this.map?.off(type, [id], map[id]);
        delete map[id];
      }
    }
  }

  removeType(type: string) {
    const map = this.listenerMap.get(type);
    if (map) {
      for (const id in map) {
        this.map?.off(type, [id], map[id]);
      }
    }
    this.listenerMap.delete(type);
  }

  removeAll() {
    this.listenerMap.forEach((map, type) => {
      for (const id in map) {
        this.map?.off(type, [id], map[id]);
      }
    });
    this.listenerMap.clear();
  }
}

export default Listener;
export type { Handler };
